/*
 * Copyright (c) 2021 Talkweb Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "iot_i2c.h"
#include "u8g2.h"
#include "u8x8.h"

#include "oled_u8g2.h"
#include "boot_animation.h"

volatile osSemaphoreId_t g_gpioIsrSemId = NULL;
volatile char g_startBootAnimationFlag = 0;

static u8g2_t u8g2;

I2C_SEND_STATES g_i2cSendState = STATE_SEND_IDLE;

#define MAX_DATAS 1200
#define EVERY_DATA_SEND 16
unsigned char g_send_data[2] = {0};
unsigned char g_send_datas[MAX_DATAS] = {0};

//OLED通信端口
#define WIFI_IOT_I2C_IDX_0 0
#define OLED_I2C_BAUDRATE (100 * 1000) // 400k 频率
#define SSD1306_I2C_ADDR (0x3C << 1)
#define SSD1306_CTRL_CMD 0x00
#define SSD1306_CTRL_DATA 0x40
#define FEATURE_ISQ_CONTROLL 1

//信号管脚
#define WIFI_IOT_IO_NAME_GPIO_13 13
#define WIFI_IOT_IO_NAME_GPIO_14 14

static uint32_t HALI2cWriteDatas(unsigned int id, uint16_t device_addr, uint32_t send_len)
{
    unsigned int status;
    status = IoTI2cWrite(id, device_addr, g_send_datas, send_len);

    if (status != 0)
    {
        printf("===== Error: I2C write status = 0x%x! =====\r\n", status);
        return status;
    }
    return 0;
}

/**
 * SSD1306写data数据
 */
static void WriteIICDatas(unsigned char *IIC_Datas, uint32_t length)
{
    memset_s(g_send_datas, MAX_DATAS, 0, MAX_DATAS);
    g_send_datas[0] = SSD1306_CTRL_DATA;
    memcpy_s(g_send_datas + 1, MAX_DATAS, IIC_Datas, length);
    HALI2cWriteDatas(WIFI_IOT_I2C_IDX_0, SSD1306_I2C_ADDR, length + 1);
}

/**
 * SSD1306写CMD数据
 */
static void WriteIICCmds(unsigned char *IIC_Datas, uint32_t length)
{
    memset_s(g_send_datas, MAX_DATAS, 0, MAX_DATAS);
    g_send_datas[0] = SSD1306_CTRL_CMD;
    memcpy_s(g_send_datas + 1, MAX_DATAS, IIC_Datas, length);
    HALI2cWriteDatas(WIFI_IOT_I2C_IDX_0, SSD1306_I2C_ADDR, length + 1);
}

static void HalDelayMs(uint32_t ms)
{
    uint32_t msPerTick = 1000 / osKernelGetTickFreq(); // 10ms
    if (ms >= msPerTick)
    {
        osDelay((ms / msPerTick));
    }

    uint32_t restMs = ms % msPerTick;
    if (restMs > 0)
    {
        usleep(restMs * 1000);
    }
}

static void HalI2cInit()
{
    IoTGpioInit(WIFI_IOT_IO_NAME_GPIO_13);
    IoTGpioInit(WIFI_IOT_IO_NAME_GPIO_14);
    IoTIoSetFunc(WIFI_IOT_IO_NAME_GPIO_13, IOT_IO_FUNC_GPIO_13_I2C0_SDA);
    IoTIoSetFunc(WIFI_IOT_IO_NAME_GPIO_14, IOT_IO_FUNC_GPIO_14_I2C0_SCL);
    IoTI2cInit(WIFI_IOT_I2C_IDX_0, OLED_I2C_BAUDRATE);
}

uint8_t U8g2Hi3861I2cByteCb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    uint8_t *data;
    uint8_t index;
    switch (msg)
    {
    case U8X8_MSG_BYTE_SEND:
        data = (uint8_t *)arg_ptr;
        if (g_i2cSendState == STATE_SEND_START && arg_int == 1)
        {
            if (*data == 0x00)
            {
                g_i2cSendState = STATE_SEND_CMD;
                break;
            }
            else if (*data == 0x40)
            {
                g_i2cSendState = STATE_SEND_DATA;
                break;
            }
            else
            {
                // do nonthing
            }
        }

        if (g_i2cSendState == STATE_SEND_CMD)
        {
            WriteIICCmds(data, arg_int);
        }
        else if (g_i2cSendState == STATE_SEND_DATA)
        {
            WriteIICDatas(data, arg_int);
        }
        else
        {
            // do nothing
        }
        break;

    case U8X8_MSG_BYTE_INIT:
        HalI2cInit();
        break;

    case U8X8_MSG_BYTE_START_TRANSFER:
        if (g_i2cSendState == STATE_SEND_IDLE)
        {
            g_i2cSendState = STATE_SEND_START;
        }
        else
        {
            g_i2cSendState = STATE_SEND_IDLE;
        }
        break;
    case U8X8_MSG_BYTE_END_TRANSFER: // send all at once
        if (g_i2cSendState == STATE_SEND_CMD || g_i2cSendState == STATE_SEND_DATA)
        {
            g_i2cSendState = STATE_SEND_IDLE;
        }
        break;
    default:
        return 0;
    }
    return 1;
}

uint8_t U8g2Hi3861GpioAndDelayCb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    switch (msg)
    {
    case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second
        __asm__ volatile("nop");
        break;
    case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
        __asm__ volatile("nop");
        break;
    case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
        usleep(10);
        break;
    case U8X8_MSG_DELAY_MILLI:
        HalDelayMs(arg_int);
        break;
    case U8X8_MSG_GPIO_AND_DELAY_INIT:
        break;
    case U8X8_MSG_GPIO_RESET:
        break;
    case U8X8_MSG_GPIO_I2C_DATA:
        break;
    case U8X8_MSG_GPIO_I2C_CLOCK:
        break;
    }
    return 0;
}

static unsigned int g_lastTickCount = 0;

static void GpioSsdIsrFunc(char *arg)
{
    (void)arg;

    //消除按键抖动
    unsigned int tickCount = osKernelGetTickCount();
    unsigned int count = tickCount - g_lastTickCount;
    g_lastTickCount = tickCount;

    if (count > 50)
    {
        g_startBootAnimationFlag ^= 1;
        osSemaphoreRelease(g_gpioIsrSemId);
    }

    return;
}

/**
 * 初始化开发板上的按键GPIO中断
 */
static void InitOledKeyGpioIsr(void)
{
    g_gpioIsrSemId = osSemaphoreNew(32, 0, NULL);

    IoTGpioInit(IOT_GPIO_IO_GPIO_5);
    IoTIoSetFunc(IOT_GPIO_IO_GPIO_5, IOT_IO_FUNC_GPIO_5_GPIO);
    IoTIoSetPull(IOT_GPIO_IO_GPIO_5, IOT_IO_PULL_UP);

    uint32_t ret = 0;
    ret = IoTGpioSetDir(IOT_GPIO_IO_GPIO_5, IOT_GPIO_DIR_IN);
    if (ret != 0)
    {
        printf("===== ERROR ======gpio -> hi_gpio_set_dir1 ret:%d\r\n", ret);
        return;
    }
    ret = IoTGpioRegisterIsrFunc(IOT_GPIO_IO_GPIO_5, IOT_INT_TYPE_EDGE, IOT_GPIO_EDGE_RISE_LEVEL_HIGH, GpioSsdIsrFunc, NULL);

    return;
}

/**
 *  绘制HarmonyOS开机动画
 */
static void DrawHarmonyOSAnimation(void)
{
    for (int i = 0; i < 58; i++)
    {
        u8g2_FirstPage(&u8g2);
        do
        {
            u8g2_DrawXBMP(&u8g2, 0, 0, 128, 64, openHarmony_All[i]);
        } while (u8g2_NextPage(&u8g2));
    }
}

/**
 * 绘制Talkweb LOGO，未来已来,远方不远
 */
static void DrawTalkwebLogo(void)
{
    u8g2_FirstPage(&u8g2);
    do
    {
        u8g2_DrawXBMP(&u8g2, 0, 0, 128, 64, talkweb_end);
    } while (u8g2_NextPage(&u8g2));
}

static void OledU8g2Task(void)
{
    //初始化OLED开发板上的按键中断事件
    InitOledKeyGpioIsr();

    //U8G2_R0：屏幕旋转方向，横屏不旋转。
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, U8g2Hi3861I2cByteCb, U8g2Hi3861GpioAndDelayCb);

    //初始OLED显示器，将向OLED显示器发送init消息
    //此函数会触发两种操作，一种是OLED硬件初始，一种是OLED显示屏初始清屏。
    u8g2_InitDisplay(&u8g2);

    //开启省电模式
    u8g2_SetPowerSave(&u8g2, 0);

    while (1)
    {
        DrawTalkwebLogo();
        osSemaphoreAcquire(g_gpioIsrSemId, osWaitForever);

        while (g_startBootAnimationFlag)
        {
            DrawHarmonyOSAnimation();
            osDelay(300U);
            DrawTalkwebLogo();
            osDelay(300U);
        }
    }
}

bool StartBootAnimation()
{
    osThreadAttr_t attr;
    attr.name = "OledU8g2Task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024 * 10;
    attr.priority = osPriorityNormal;
    if (osThreadNew((osThreadFunc_t)OledU8g2Task, NULL, &attr) == NULL)
    {
        printf("[Talkweb Niobe] Falied to create OledU8g2Task!\n");
        return false;
    }
    return true;
}
